import WebGl from '@/modules/three';
import utils from '@/modules/utils';
const { Three, TWEEN, dat, Stats, OrbitControls, OBJLoader, MTLLoader } = WebGl;

class WebGlControl {
  private container: any;
  private camera: any;
  private scene: any;
  private renderer: any;
  private stats: any;
  private animateFrame: any;
  private tween: any;
  private controls: any;
  private clock: any = new Three.Clock();

  private Geometrys: {
    eyeBall?: any;
  } = {}; //保存所有Geometry，方便统一销毁
  private Materials: {
    hairMaterial?: any;
    faceMaterial?: any;
    eyeMaterial?: any;
    noseMaterial?: any;
    mouseMaterial?: any;
    earMaterial?: any;
    eyeBallMaterial?: any;
  } = {}; //保存所有Material，方便统一销毁
  private Meshs: {
    avatar?: any;
    eyeball?: any;
    eyeballr?: any;
  } = {};
  private avatarGroup: any;

  private GuiInit: any = null;
  private guiData: any = {
    x: 0.005,
    // faceColor: '#e9c995',
    // hairColor: '#eda02e',
    faceColor: '#eaceb8',
    hairColor: '#f2ebe0',
    eyeColor: '#efefef',
    noseColor: '#deab91',
    mouseColor: '#e5baa2',
    earColor: '#e0ab93',
    eyeBallColor: '#233434',
    autoRotate: true,
    targetPoint: new Three.Vector3(0, 0, 20),
    eyeOriginal: new Three.Vector3(-4, -2.5, 8),
    eyeOriginalr: new Three.Vector3(4, -2.5, 8), //圆心； 半径≈2
  };
  private containerInfo: {
    width: number;
    height: number;
    x: number;
    y: number;
  } = { width: 0, height: 0, x: 0, y: 0 };
  private mouseData: any = {
    mouseX: 0,
    mouseY: 0,
  };
  private pointer = new Three.Vector2(-2, -2); //初始化在[-1,1]以外
  public inputFocus: boolean = false;
  private inputSelecttionPosi: number = 0;

  public init = (id: string) => {
    // if(this.scene) {return}
    this.container = document.getElementById(id);
    (this.containerInfo.width = this.container.clientWidth),
      (this.containerInfo.height = this.container.clientHeight);

    const pos = utils.element_position(this.container);
    this.containerInfo.x = pos.x;
    this.containerInfo.y = pos.y;

    this.scene = new Three.Scene();

    this.camera = new Three.PerspectiveCamera(
      60,
      this.containerInfo.width / this.containerInfo.height,
      1,
      500,
    );
    this.camera.position.x = 0;
    this.camera.position.y = 2;
    this.camera.position.z = 30;
    this.camera.lookAt(this.scene.position);

    this.renderer = new Three.WebGLRenderer({
      alpha: true,
    });
    // this.renderer.setClearColor( 0xffffff );
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.renderer.setSize(this.containerInfo.width, this.containerInfo.height);
    this.container.appendChild(this.renderer.domElement);

    // this.controls = new OrbitControls(this.camera, this.renderer.domElement);
    // this.controls.enableDamping = true; //阻尼
    // this.controls.dampingFactor = 0.1; //阻尼

    this.addThings();
    this.addLight(); //+光源
    this.animate(); //开始动
    this.initStats();
    this.initEventListen();

    this.initGui();
  };
  public destroy = () => {
    this.toggleGui(0);
    Object.values(this.Geometrys).forEach((e) => {
      e.dispose();
    });
    Object.values(this.Materials).forEach((e) => {
      e.dispose();
    });
    this.scene.clear();
    this.renderer.dispose();
    this.renderer.forceContextLoss();
    this.camera = null;
    this.scene = null;
    this.renderer = null;
    this.GuiInit = null;
    this.stats = null;
    this.avatarGroup = null;
    if (this.tween) {
      this.tween.stop();
      this.tween = null;
    }
    cancelAnimationFrame(this.animateFrame);
  };

  private initStats = () => {
    // this.stats = new Stats();
    // this.stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
    // this.stats.dom.style.position = 'absolute';
    // this.container.appendChild(this.stats.dom);
  };

  public initGui = () => {
    if (this.GuiInit) {
      this.toggleGui();
      return;
    }
    this.GuiInit = new dat.GUI({
      // autoPlace: false
    });

    this.GuiInit.domElement.style.position = 'absolute';
    this.GuiInit.domElement.style.right = '10px';
    this.GuiInit.domElement.style.top = '10px';

    let GuiController = this.GuiInit.addFolder('控制窗口');
    // GuiController.add(this.guiData, 'x', -0.1, 0.1, 0.005).name('自转速度');
    GuiController.addColor(this.guiData, 'faceColor').name('脸颜色');
    GuiController.addColor(this.guiData, 'hairColor').name('头发颜色');
    GuiController.addColor(this.guiData, 'eyeColor').name('眼睛颜色');
    GuiController.addColor(this.guiData, 'noseColor').name('鼻子颜色');
    GuiController.addColor(this.guiData, 'mouseColor').name('嘴颜色');
    GuiController.addColor(this.guiData, 'earColor').name('耳朵颜色');
    GuiController.open();

    // let cubeGui = this.GuiInit.addFolder("cube position");
    // cubeGui.open()
  };

  public toggleGui = (state: any = null) => {
    if (!this.GuiInit || !this.GuiInit.domElement) {
      return;
    }
    let display = this.GuiInit.domElement.style.display == 'none' ? '' : 'none';
    if (state == 0) {
      display = 'none';
    }
    this.GuiInit.domElement.style.display = display;
  };
  private initEventListen = () => {
    window.addEventListener('pointermove', this.onPointerMove);
    window.addEventListener('resize', this.onWindowResize);
  };
  private onPointerMove = (event) => {
    if (event.isPrimary === false) return;
    // this.mouseData.mouseX = event.clientX - this.container.offsetLeft;
    // this.mouseData.mouseY = event.clientY - this.container.offsetTop;

    var scrollTop = window.pageYOffset || document.documentElement.scrollTop; //在container中的位置要考虑scroll

    this.mouseData.mouseX =
      event.clientX - this.containerInfo.x - this.containerInfo.width * 0.5;
    this.mouseData.mouseY =
      event.clientY +
      scrollTop -
      this.containerInfo.y -
      this.containerInfo.height * 0.5;
  };

  private onWindowResize = () => {
    this.containerInfo.width = this.container.clientWidth;
    this.containerInfo.height = this.container.clientHeight;
    const pos = utils.element_position(this.container);
    this.containerInfo.x = pos.x;
    this.containerInfo.y = pos.y;
  };

  public inputStateChange = (start: number | null, end?: number | null) => {
    // selection 光标开始位置 - 光标结束位置
    this.inputSelecttionPosi = start || 0;
  };

  // private getNumericStyleProperty = (style, prop) => {
  //   return parseInt(style.getPropertyValue(prop),10) ;
  // }

  // private element_position = (e) => {
  //     var x = 0, y = 0;
  //     var inner = true ;
  //     do {
  //         x += e.offsetLeft;
  //         y += e.offsetTop;
  //         var style = getComputedStyle(e,null) ;
  //         var borderTop = this.getNumericStyleProperty(style,"border-top-width") ;
  //         var borderLeft = this.getNumericStyleProperty(style,"border-left-width") ;
  //         y += borderTop ;
  //         x += borderLeft ;
  //         if (inner){
  //           var paddingTop = this.getNumericStyleProperty(style,"padding-top") ;
  //           var paddingLeft = this.getNumericStyleProperty(style,"padding-left") ;
  //           y += paddingTop ;
  //           x += paddingLeft ;
  //         }
  //         inner = false ;
  //     } while (e = e.offsetParent);
  //     return { x: x, y: y };
  // }

  private addLight = () => {
    let ambi = new Three.AmbientLight(0x686868);
    ambi.intensity = 2.6;
    this.scene.add(ambi);
    let dirLight = new Three.DirectionalLight(0xffffff);
    dirLight.position.set(-4, 0, 5);
    dirLight.intensity = 0.6;
    this.scene.add(dirLight);

    let dirLight2 = new Three.DirectionalLight(0xcdcdcd);
    dirLight2.position.set(-4, 0, -1);
    dirLight2.intensity = 0.45;
    this.scene.add(dirLight2);
  };

  private addThings = () => {
    // const axesHelper = new Three.AxesHelper( 800 );
    // this.scene.add( axesHelper )  //添加坐标轴；红线是X轴，绿线是Y轴，蓝线是Z轴

    this.avatarGroup = new Three.Group();
    this.avatarGroup.lookAt(this.guiData.targetPoint);
    this.scene.add(this.avatarGroup);

    this.addAvatar();
    this.addEyeball();

    this.initTween();
  };

  private addAvatar = () => {
    this.Materials.hairMaterial = new Three.MeshToonMaterial({
      color: this.guiData.hairColor,
    });
    this.Materials.faceMaterial = new Three.MeshToonMaterial({
      color: this.guiData.faceColor,
    });
    this.Materials.eyeMaterial = new Three.MeshToonMaterial({
      color: this.guiData.eyeColor,
    });
    this.Materials.noseMaterial = new Three.MeshToonMaterial({
      color: this.guiData.noseColor,
    });
    this.Materials.mouseMaterial = new Three.MeshToonMaterial({
      color: this.guiData.mouseColor,
    });
    this.Materials.earMaterial = new Three.MeshToonMaterial({
      color: this.guiData.earColor,
    });
    // let mtlLoader = new MTLLoader();
    // mtlLoader.load('../models/stars.mtl', (materials) => {
    //   materials.preload();
    let objLoader = new OBJLoader();
    // objLoader.setMaterials(materials);
    objLoader.load(
      '../models/babyHead.obj',
      (object) => {
        object.traverse((child) => {
          // console.log('obj', child);
          if (child.name == 'face') {
            child.material = this.Materials.faceMaterial;
          }
          if (child.name == 'hair') {
            child.material = this.Materials.hairMaterial;
          }
          if (child.name == 'eye') {
            child.material = this.Materials.eyeMaterial;
          }
          if (child.name == 'nose') {
            child.material = this.Materials.noseMaterial;
          }
          if (child.name == 'mouse') {
            child.material = this.Materials.mouseMaterial;
          }
          if (child.name == 'ear') {
            child.material = this.Materials.earMaterial;
          }
          if (child instanceof Three.Mesh) {
            object.receiveShadow = true;
            object.castShadow = true;
          }
        });
        object.position.set(0, -5, 0);
        object.receiveShadow = true;
        object.castShadow = true;
        this.Meshs.avatar = object;
        this.avatarGroup.add(this.Meshs.avatar);
      },
      (xhr) => {
        console.log((xhr.loaded / xhr.total) * 100 + '% loaded');
      },
      (error) => {
        console.log(error);
      },
    );
    // });
  };
  private addEyeball = () => {
    this.Geometrys.eyeBall = new Three.SphereGeometry(0.4, 12, 12);
    this.Materials.eyeBallMaterial = new Three.MeshToonMaterial({
      color: this.guiData.eyeBallColor,
    });
    this.Meshs.eyeball = new Three.Mesh(
      this.Geometrys.eyeBall,
      this.Materials.eyeBallMaterial,
    );
    this.Meshs.eyeballr = this.Meshs.eyeball.clone();
    this.Meshs.eyeball.position.copy(
      this.guiData.eyeOriginal.clone().add(new Three.Vector3(0, 0, 2)),
    );
    this.Meshs.eyeballr.position.copy(
      this.guiData.eyeOriginalr.clone().add(new Three.Vector3(0, 0, 2)),
    );
    this.avatarGroup.add(this.Meshs.eyeball, this.Meshs.eyeballr);
  };

  private initTween = () => {};

  private VecZoom = new Three.Vector3(4.5, 4.5, 1); //角度+4.5倍
  private calcEyeBall = () => {
    if (this.Meshs.eyeball) {
      const add = this.guiData.targetPoint
        .clone()
        .multiply(this.VecZoom)
        .normalize()
        .multiplyScalar(2);
      const pos1 = this.guiData.eyeOriginal.clone().add(add);
      const pos2 = this.guiData.eyeOriginalr.clone().add(add);
      this.Meshs.eyeball.position.copy(pos1);
      this.Meshs.eyeballr.position.copy(pos2);
    }
  };

  private animate = () => {
    // if(!this.renderer){return;}
    if (this.stats) {
      this.stats.begin();
    }

    this.render();
    TWEEN.update();
    // this.controls.update();

    if (this.stats) {
      this.stats.end();
    }
    this.animateFrame = requestAnimationFrame(this.animate);
  };
  private render = () => {
    if (this.Materials.hairMaterial) {
      this.Materials.hairMaterial.color.set(this.guiData.hairColor);
      this.Materials.faceMaterial.color.set(this.guiData.faceColor);
      this.Materials.eyeMaterial.color.set(this.guiData.eyeColor);
      this.Materials.noseMaterial.color.set(this.guiData.noseColor);
      this.Materials.mouseMaterial.color.set(this.guiData.mouseColor);
      this.Materials.earMaterial.color.set(this.guiData.earColor);
    }
    if (this.inputFocus) {
      const x = this.inputSelecttionPosi - 12; //12为中心
      this.guiData.targetPoint.lerp(new Three.Vector3(x * 0.3, -3, 20), 0.2);
    } else {
      this.guiData.targetPoint.lerp(
        new Three.Vector3(
          this.mouseData.mouseX / 60,
          this.mouseData.mouseY / -65,
          20,
        ),
        0.2,
      );
    }
    this.avatarGroup.lookAt(this.guiData.targetPoint);
    this.calcEyeBall();

    this.renderer.render(this.scene, this.camera);
  };
}

const webGlControl = new WebGlControl();
export default webGlControl;
